<?php
/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Chat Server
// Project: molten-chat
// Developed by: Matthew Oltendorf
// Version: a2.1
// Updated on: August 09th, 2009
// Copyright (c) 2008-2009 Matthew Oltendorf
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */

// PHP: Hypertext Preprocessor Document

/*
	Unfinished Items:
		1.	Database Abstraction Layer still needs to be developed.
		3.	push () should have some form of min/max update speed system that bases its update speed on how much CPU usage is occuring.
		4.	Doesn't properly function the way I would want with multiple rooms at one time, needs altering.
		5.	
*/

/*
	Known Issues:
		1.	Does not work on load balanced server clusters; must be run on a single server. This is due to the shared memory cache.
		2.	connect doesn't always successfully connect to a valid room that does exist.
		3.
		4.
		5.
*/

// Style: Object Oriented.

/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
// Name: molten_chat_server
// Type: Singleton.
// Title: MoltX Chat Server Object.
// Description: Designed to be included and used to prepare XML data to be sent to the MoltX Chat Ajax Object.
// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
final class molten_chat_server extends molten_chat_module
	{
		private static
			$characters = array ('1', '2', '3', '4', '5', '6', '7', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'),
			$comments = array (),
			$connected = false,
			$directory = molten_chat_rooms,
			$extension = '.room.php',
			$formatting = array (
				'data' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\nreturn %1\$s;\n",
				'first_log' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\n\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'message' => '%6\$s');\n\$message_log [] = array ('line' => %5\$u, 'time' => %2\$u, 'message' => '%4\$s');\n",
				'log' => "<?php\nif (\n\t!defined ('molten_chat')\n)\n\t{\n\t\tdie ('Not authorised.');\n\t}\n\n\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n\n\$message_log = array_merge (\$message_log, %5\$s);",
				'message' => "\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'user' => '%3\$s', 'message' => '%4\$s');\n",
				'message_link' => "\$message_log [] = array ('line' => %1\$u, 'link' => array ('local' => %5\$s, 'uri' => %6\$s), 'time' => %2\$u, 'user' => '%3\$s', 'message' => '%4\$s');\n",
				'message_system' => "\$message_log [] = array ('line' => %1\$u, 'time' => %2\$u, 'message' => '%4\$s');\n",
				'message_system_link' => "\$message_log [] = array ('line' => %1\$u, 'link' => array ('local' => %5\$s, 'uri' => %6\$s), 'time' => %2\$u, 'message' => '%4\$s');\n",
				'message_header' => "\$message_header = array ('time' => %2\$u, 'message' => '%4\$s');\n"
			),
			$identifiers = array (),
			$initialized = false,
			$instances = 2,
			$lock = array (
				'global' => array (
					'pointer' => false,
					'switch' => false
				),
				'memory' => array (
					'pointer' => false,
					'switch' => false
				),
				'room' => array (
					'pointer' => false,
					'switch' => false
				),
				'users' => array (
					'pointer' => false,
					'switch' => false
				)
			),
			$room = array (
				'data' => array (
					'active' => false
				),
				'files' => array (),
				'temporary' => array (
					'current' => 0,
					'level' => 0,
					'user' => 0
				)
			),
			$threshold = 300, // Run garbage collection every five minutes.
			$update = 30;
		
		public static
			$language = array (
				'cache_error' => 'There was an error using caching module: %1$s.',
				'database_error' => 'There was an error connecting to the database.',
				'log_owned' => 'Room created on %2$s at %3$s by %4$s.',
				'log_system' => 'Room was automatically generated on %2$s at %3$s.',
				'new_announcement' => 'New announcement',
				'no_announcement' => 'Announcement removed.',
				'not_contiguous_log' => 'There was an error retrieving a contiguous chat history.',
				'room_connected' => 'Connected.',
				'room_disconnected' => 'Disconnected.',
				'room_not_exist' => 'This room does not exist.',
				'system' => 'system',
				'too_many_instances' => 'You have too many open instances.'
			);

		public static function debug ()
			{
				$instances = self :: global_memory_get ('instances');

				echo '<pre>', var_export ($instances, true), '</pre>';
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: __construct
		// Title: Class construction.
		// Variables: None.
		// Description: Disabled.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private function __construct ()
			{
				// No further actions required.
				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: __destruct
		// Title: Class destruction.
		// Variables: None.
		// Description: Disabled.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private function __destruct ()
			{
				// No further actions required.
				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: initialize
		// Title: Prepare objects.
		// Variables: None.
		// Description: Communicate with other external classes that are part of the QuickChat Project to syncronize them.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function initialize ()
			{
				// This function can only be run once.
				if (
					self :: $initialized
				)
					{
						return false;
					}

				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				$list = get_class_vars (__CLASS__);
				$variables = array ();

				// Create an array of references.
				foreach ($list as $variable => $value)
					{
						$variables [$variable] = &self :: $$variable;
					}

				try
					{
						// Assign data to database.
						molten_chat_database :: core_initialize ($variables);

						// Assign data to cache core.
						molten_chat_cache_core :: core_initialize ($variables);

						// Assign data to cache.
						molten_chat_cache :: core_initialize ($variables);

						// Assign data to cache.
						molten_chat_module :: core_initialize ($variables);

						// Assign data to slash commands.
						molten_chat_functions_core :: initialize ($variables);

						self :: $instances = (integer) molten_chat_configuration :: $instances;
						self :: $instances = (self :: $instances >= 0) ?
							self :: $instances :
							2;

						self :: $update = (integer) molten_chat_configuration :: $update;
						self :: $update = (self :: $update >= 0) ?
							self :: $update :
							2;

						// Run garbage collection.
						self :: collect_garbage ();
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						return false;
					}

				// We are properly initialized.
				self :: $initialized = true;

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: create
		// Title: Create a room.
		// Variables: $room, $use, $owner, $levels, $messages, $retrieve, $age, $is_input
		//	1.	$room: The room to create.
		//	2.	$use: Whether or not to automatically select the room after it is created.
		//		Default: false
		//	3.	$owner: The owner of the room. If this option is left blank, it is considered a public room.
		//		Default: false
		//	4.	$levels: The maximum number of level files to maintain; relaxed if the age of a level is less than or equal to the maximum age of levels.
		//		Default: 5
		//	5.	$messages: The maximum number of messages per level.
		//		Default: 50
		//	6.	$retrieve: The maximum number of messages to retrieve on fresh connections.
		//		Default: 100
		//	7.	$age: The maximum age (in seconds) to keep a level file; relaxed if there are as many as or less levels than the maximum levels in existance.
		//		Default: 300
		//	8.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Create a new room, if the room does not exist, it returns false and does nothing (not even automatically select the room).
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function create ($room_name, $use = false, $owner = false, $levels = 5, $messages = 50, $retrieve = 100, $age = 300, $is_input = true)
			{
				// Check if we are properly initialized.
				if (
					!self :: $initialized
				)
					{
						return false;
					}

				// Debug.
				self :: $comments [] = 'create: called';

				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean room name.
						$room_name = $is_input ?
							self :: clean_data ($room_name) :
							$room_name;

						// Typecast the room name to string.
						$room_name = (string) $room_name;

						// Cast $owner to a type we accept.
						$owner = is_string ($owner) ?
							$owner :
							false;

						// Clean username.
						$owner = $is_input ?
							self :: clean_data ($owner) :
							$owner;

						// Clean other input by typecasting it.
						$use = (boolean) $use;
						$levels = (integer) $levels;
						$messages = (integer) $messages;
						$retrieve = (integer) $retrieve;
						$age = (integer) $age;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				// Lock memory.
				molten_chat_cache :: lock_memory (true);

				// Get a list of active rooms.
				$rooms = molten_chat_cache :: global_memory_get ('rooms');

				// Check if the memory cache is populated.
				if (
					is_null ($rooms)
				)
					{
						$rooms = array ();

						// Query the database.
						$sql = 'SELECT room_name
							FROM ' . molten_chat_rooms_table;

						// Check for connection.
						if (
							molten_chat_database :: database_connect ()
						)
							{
								$result = molten_chat_database :: query ($sql);

								// Convert query into an indexed array of objects.
								$result = molten_chat_database :: get_array ($result);
							}
					}

				// Generate safe encoding of room name to be used as a filename.
				$room_base = self :: parse_file ($room_name);

				// Generate a direct link to the room file.
				$room_file = self :: $directory . $room_base . '.data' . self :: $extension;

				// Open and/or create file.
				$pointer = fopen ($room_file, 'a+');
				
				// Acquire lock without blocking.
				while (
					!flock ($pointer, LOCK_EX | LOCK_NB)
				)
					{
						// Generate a random number.
						$random = mt_rand (0, 250);

						// Sleep for 0.000 to 0.250 seconds. seconds.
						usleep (1000 * $random);
					}

				// Check if this file indeed contains something other than gibberish.
				$exists = (fread ($pointer, (5)) == '<?php');

				if (
					!$exists
				)
					{
						$created = time ();

						$input = array (
							$room_name,
							date ('l, F jS, Y', $created),
							date ('H:i:s', $created),
							(($owner !== false) ?
								$owner :
								self :: $language ['system'])
						);

						// Grab header contents to append.
						$message = ($owner !== false) ?
							vsprintf (self :: $language ['log_owned'], $input) :
							vsprintf (self :: $language ['log_system'], $input);

						$data = array (
							'active' => true,
							'announcement' => false,
							'age' => $age,
							'base' => $room_base,
							'created' => $created,
							'header' => $message,
							'levels' => $levels,
							'messages' => $messages,
							'retrieve' => $retrieve,
							'name' => $room_name,
							'owner' => $owner
						);

						$export = var_export ($data, true);

						$export = sprintf (self :: $formatting ['data'], $export);

						// Write data contents to new room descriptor.
						fwrite ($pointer, $export);

						// Generate path to room's first log file (level 1).
						$room_log = self :: $directory . $room_base . '.0' . self :: $extension;

						$input = array (
							0,
							$created,
							($owner !== false) ?
								self :: parse_data ($owner) :
								'false',
							self :: parse_data ($message),
							1,
							self :: parse_data (self :: $language ['room_connected'])
						);

						$header = vsprintf (self :: $formatting ['first_log'], $input);

						$log = fopen ($room_log, 'w'); // Open file for writing.

						// Acquire lock without blocking.
						while (
							!flock ($log, LOCK_EX | LOCK_NB)
						)
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}
						fwrite ($log, $header . "\n"); // Write data.
						flock ($log, LOCK_UN); // Unlock.

						fclose ($log); // Close file.

						if (
							$use
						)
							{
								// Set room data.
								self :: $room = array (
									'data' => $data,
									'files' => array (
										'data' => $room_file,
										'room' => self :: $directory . $room_base . '.room.lock',
										'users' => self :: $directory . $room_base . '.users.lock',
										'logs' => array ($room_log)
									)
								);

								$current = !is_null ($current = molten_chat_cache :: room_memory_get ('current')) ?
									$current :
									self :: get_current ();
								$level = &$current ['level'];
								$line = &$current ['line'];

								// Check if the room was not updated by create.
								if (
									$level == 0 and
									$line == 0
								)
									{
										$current = array (
											'level' => 0,
											'line' => 2
										);

										// Update the room with the new line count.
										molten_chat_cache :: room_memory_set ('current', $current);
									}
							}
					}

				// Check if our room is already somehow in memory.
				if (
					!in_array ($room_name, $rooms)
				)
					{
						// Check for connection.
						if (
							molten_chat_database :: database_connect ()
						)
							{
								// Update database.
								$sql = 'INSERT INTO ' . molten_chat_rooms_table . '
									(room_active, room_age, room_levels, room_level, room_messages, room_name, room_owner, room_activity, room_creation)
									VALUES
									(\'true\', ' . $age . ', ' . $levels . ', 0, ' . $messages . ', ' . (($owner !== false) ? '\'' . $owner . '\'' : 'NULL') . ', ' . time () . ', ' . time () . ')';

								molten_chat_database :: query ($sql, false);
							}

						// Append room to the current list of rooms active.
						$rooms [] = $room_name;

						// Add data to memory.
						molten_chat_cache :: global_memory_set ('rooms', $rooms);
					}

				// Close the file.
				flock ($pointer, LOCK_UN);
				fclose ($pointer);

				// Release memory lock.
				molten_chat_cache :: lock_memory (false);

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				// Return whether or not this room exists.
				return !$exists;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: connect
		// Title: Use a room.
		// Variables: $room_name, $is_input
		//	1.	$room_name: The room to join.
		//	2.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Select a room to use throughout the object.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function connect ($room_name, $is_input = true)
			{
				// Check if we are properly initialized.
				if (
					!self :: $initialized
				)
					{
						return false;
					}

				// Debug.
				self :: $comments [] = 'connect: called';
				
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean room name.
						$room_name = $is_input ?
							self :: clean_data ($room_name) :
							$room_name;

						// Typecast the room name to string.
						$room_name = (string) $room_name;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				// Get a list of active rooms.
				$rooms = molten_chat_cache :: global_memory_get ('rooms');

				// Check if the memory cache is populated.
				if (
					is_null ($rooms)
				)
					{
						$rooms = array ();

						// Check for connection.
						if (
							molten_chat_database :: database_connect ()
						)
							{
								// Query the database.
								$sql = 'SELECT room_name
									FROM ' . molten_chat_rooms_table;

								$result = molten_chat_database :: query ($sql);

								// Convert query into an indexed array of objects.
								$result = molten_chat_database :: get_array ($result);
							}
					}

				// Check if our room exists.
				if (
					in_array ($room_name, $rooms)
				)
					{
						// Generate safe encoding of room name to be used as a filename.
						$room_base = self :: parse_file ($room_name);

						// Generate a direct link to the room file.
						$room_file = self :: $directory . $room_base . '.data' . self :: $extension;

						// Open and/or create file.
						$pointer = fopen ($room_file, 'a+');

						// Acquire lock without blocking.
						while (
							!flock ($pointer, LOCK_SH | LOCK_NB)
						)
							{
								// Generate a random number.
								$random = mt_rand (0, 250);

								// Sleep for 0.000 to 0.250 seconds. seconds.
								usleep (1000 * $random);
							}

						$exists = (fread ($pointer, (5)) == '<?php');

						// Check if this file indeed contains something other than gibberish.
						if (
							$exists
						)
							{
								$data = include ($room_file);

								if (
									$data ['active']
								)
									{
										// Set room data.
										self :: $room = array (
											'data' => $data,
											'files' => array (
												'data' => $room_file,
												'room' => self :: $directory . $room_base . '.room.lock',
												'users' => self :: $directory . $room_base . '.users.lock',
												'logs' => array ()
											)
										);

										$current = !is_null ($current = molten_chat_cache :: room_memory_get ('current')) ?
											$current :
											self :: get_current ();
										$level = &$current ['level'];
										$line = &$current ['line'];

										// Check if the room was not updated by create.
										if (
											$level == 0 and
											$line == 0
										)
											{
												$current = array (
													'level' => 0,
													'line' => 2
												);

												// Update the room with the new line count.
												molten_chat_cache :: room_memory_set ('current', $current);
											}
									}
								else
									{
										$exists = false;
									}
							}

						// Close the file.
						flock ($pointer, LOCK_UN);
						fclose ($pointer);

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Return whether or not this room exists.
						return $exists;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: modify
		// Title: modify a room.
		// Variables: $room, $use, $owner, $levels, $messages, $retrieve, $age, $is_input
		//	1.	$room: The room to modify.
		//	2.	$active: Whether or not the room is active.
		//		Default: null
		//	3.	$owner: The owner of the room.
		//		Default: null
		//	4.	$levels: The maximum number of level files to maintain; relaxed if the age of a level is less than or equal to the maximum age of levels.
		//		Default: null
		//	5.	$messages: The maximum number of messages per level.
		//		Default: null
		//	6.	$retrieve: The maximum number of messages to retrieve on fresh connections.
		//		Default: null
		//	7.	$age: The maximum age (in seconds) to keep a level file; relaxed if there are as many as or less levels than the maximum levels in existance.
		//		Default: null
		//	8.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Create a new room, if the room does not exist, it returns false and does nothing (not even automatically select the room).
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function modify ($room_name, $active = null, $owner = null, $levels = null, $messages = null, $retrieve = null, $age = null, $is_input = true)
			{
				// Check if we are properly initialized.
				if (
					!self :: $initialized
				)
					{
						return false;
					}

				// Debug.
				self :: $comments [] = 'connect: called';

				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean room name.
						$room_name = $is_input ?
							self :: clean_data ($room_name) :
							$room_name;

						// Typecast the room name to string.
						$room_name = (string) $room_name;

						// Cast $owner to a type we accept.
						$owner = is_string ($owner) ?
							$owner :
							null;

						// Clean username.
						$owner = $is_input ?
							self :: clean_data ($owner) :
							$owner;

						// Clean other input by typecasting it.
						$levels = !is_null ($levels) ?
							(integer) $levels :
							null;

						$messages = !is_null ($messages) ?
							(integer) $messages :
							null;

						$retrieve = !is_null ($retrieve) ?
							(integer) $retrieve :
							null;

						$age = !is_null ($age) ?
							(integer) $age :
							null;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				// Lock memory.
				molten_chat_cache :: lock_memory (true);

				// Get a list of active rooms.
				$rooms = molten_chat_cache :: global_memory_get ('rooms');

				// Check if the memory cache is populated.
				if (
					is_null ($rooms)
				)
					{
						$rooms = array ();

						// Check for connection.
						if (
							molten_chat_database :: database_connect ()
						)
							{
								// Query the database.
								$sql = 'SELECT room_name
									FROM ' . molten_chat_rooms_table;

								$result = molten_chat_database :: query ($sql);

								// Convert query into an indexed array of objects.
								$result = molten_chat_database :: get_array ($result);
							}
					}

				// Generate safe encoding of room name to be used as a filename.
				$room_base = self :: parse_file ($room_name);

				// Generate a direct link to the room file.
				$room_file = self :: $directory . $room_base . '.data' . self :: $extension;

				// Open and/or create file.
				$pointer = fopen ($room_file, 'a+');

				// Acquire lock without blocking.
				while (
					!flock ($pointer, LOCK_EX | LOCK_NB)
				)
					{
						// Generate a random number.
						$random = mt_rand (0, 250);

						// Sleep for 0.000 to 0.250 seconds. seconds.
						usleep (1000 * $random);
					}

				// Check if this file indeed contains something other than gibberish.
				$exists = (fread ($pointer, (5)) == '<?php');

				if (
					$exists
				)
					{
						$data = include ($room_file);

						// Compare data.
						if (
							(
								!is_null ($active) and
								$active != $data ['active']
							) or
							(
								!is_null ($age) and
								$age != $data ['age']
							) or
							(
								!is_null ($levels) and
								$levels != $data ['levels']
							) or
							(
								!is_null ($messages) and
								$messages != $data ['messages']
							) or
							(
								!is_null ($retrieve) and
								$retrieve != $data ['retrieve']
							) or
							(
								!is_null ($owner) and
								$owner != $data ['owner']
							)
						)
							{
								$data = array (
									'active' => !is_null ($active) ?
										$active :
										$data ['active'],
									'announcement' => $data ['announcement'],
									'age' => !is_null ($age) ?
										$age :
										$data ['age'],
									'base' => $data ['base'],
									'created' => $data ['created'],
									'header' => $data ['header'],
									'levels' => !is_null ($levels) ?
										$levels :
										$data ['levels'],
									'messages' => !is_null ($messages) ?
										$messages :
										$data ['message'],
									'retrieve' => !is_null ($retrieve) ?
										$retrieve :
										$data ['retrieve'],
									'name' => $data ['name'],
									'owner' => !is_null ($owner) ?
										$owner :
										$data ['owner']
								);

								$export = var_export ($data, true);

								$export = sprintf (self :: $formatting ['data'], $export);

								// Wipe contents.
								ftruncate ($pointer, 0);

								// Write data contents to new room descriptor.
								fwrite ($pointer, $export);
							}
					}

				// Close the file.
				flock ($pointer, LOCK_UN);
				fclose ($pointer);

				// Release memory lock.
				molten_chat_cache :: lock_memory (false);

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: delete
		// Title: Delete room.
		// Variables: $room_name
		//	1.	$room_name: The name of the room to delete.
		// Description: Que a room for deletion.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function delete ($room_name)
			{
				// Check if we are properly initialized.
				if (
					!self :: $initialized
				)
					{
						return false;
					}

				// Debug.
				self :: $comments [] = 'connect: called';

				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				// Do stuff.

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: join
		// Title: Join a room.
		// Variables: $user, $guest, $color, $permissions, $is_input
		//	1.	$user: The user that has joined the room, if this is left blank an anonymous account is used, it is then returned.
		//		Default: false
		//	2.	$guest: Whether or not this user is a guest (no permissions).
		//		Default: true
		//	3.	$instance: Whether or not to create an instance.
		//		Default: false
		//	4.	$color: An array containing the red, green, and blue values (from 0 through 255) for the color of the user.
		//		Default: false
		//	5.	$permissions: An indexed array of what chat functions the user can use. If set to true, all permissions are enabled.
		//		Default: false
		//	6.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Adds a user to the room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function join ($user = null, $guest = true, $instance = false, $color = false, $permissions = false, $is_input = true)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							!is_null ($user) ?
								self :: to_string ($user) :
								false;

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Cast $instance to a type we accept.
						$instance = is_string ($instance) ?
							$instance :
							(($instance == true) ?
								'default' :
								false);

						// Cast $color to a type we accept.
						$color = is_array ($color) ?
							((
								isset ($color ['blue']) and
								isset ($color ['green']) and
								isset ($color ['red'])
							) ?
								((
									is_integer ($color ['blue']) and
									is_integer ($color ['green']) and
									is_integer ($color ['red'])
								) ?
									$color :
									false) :
								false) :
							(boolean) $color;

						// Cast $permissions to a type we accept.
						$permissions = is_array ($permissions) ?
							$permissions :
							(boolean) $permissions;

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						// Lock users.
						molten_chat_cache :: lock_users (true);

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;
						$increment = true;

						// Get the current users in the room.
						$users = is_array ($users = molten_chat_cache :: room_memory_get ('users')) ?
							$users :
							array ('guests' => array (), 'users' => array ('index' => array (), 'data' => array ()));

						// Check if the user was left empty.
						if (
							$user === false
						)
							{
								// What the heck are we doing here? I must have been tired. Looking into it.
								$index = array_splice ($users ['guests'], -1, 1);
								$user = (key ($index) + 1);
								$guest = true;
							}
						// Check if we are joining a guest to the room, and if it's not existing.
						elseif (
							!$guest or
							(
								is_integer ($user) and
								!isset ($user ['guests'] [$user])
							)
						)
							{
								if (
									$guest
								)
									{
										// Add guest to room. Disabled.
										//$users ['guests'] [$user] [] = array ();

										$success = true;
									}
								else
									{
										$index = array_search ($user, $users ['users'] ['index']);

										// Check if the user is already in the room.
										if (
											$index === false
										)
											{
												$success = true;

												// Add user to index.
												$users ['users'] ['index'] [] = $user;

												// Add user to room.
												$users ['users'] ['data'] [] = array (
													'color' => $color,
													'name' => $user,
													'time' => time ()
												);

												// Allow incrementing of the counter.
												$increment = true;
											}

										if (
											$instance !== false
										)
											{
												// Lock memory.
												molten_chat_cache :: lock_memory (true);

												// Get the current instances.
												$instances = is_array ($instances = molten_chat_cache :: global_memory_get ('instances')) ?
													$instances :
													array ();

												if (
													!isset ($instances [$instance])
												)
													{
														$instances [$instance] = array ();
													}

												// Add an instance to the list.
												$instances [$instance] [] = time ();

												$keys = array_keys ($instances [$instance]);
												$success = end ($keys);

												molten_chat_cache :: global_memory_set ('instances', $instances);

												// Release memory lock.
												molten_chat_cache :: lock_memory (false);

												// Do not increment the action counter.
												$increment = false;
											}
									}
							}

						if (
							$success !== false
						)
							{
								// Set the current users in the room.
								molten_chat_cache :: room_memory_set ('users', $users);

								// Increment user action count by one.
								$action_time = is_integer ($action = molten_chat_cache :: room_memory_get ('time')) ?
									$action_time :
									0;

								if (
									$action_time < (time () - self :: $update) and
									$increment
								)
									{
										// Increment user action count by one.
										$action = is_integer ($action = molten_chat_cache :: room_memory_get ('user')) ?
											$action :
											0;
										molten_chat_cache :: room_memory_set ('user', ++$action);
										molten_chat_cache :: room_memory_set ('user_time', time ());
									}
							}

						// Release users lock.
						molten_chat_cache :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						return $success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: update
		// Title: Update user.
		// Variables: $user, $guest, $color, $permissions, $is_input
		//	1.	$user: The user that has joined the room, if this is left blank an anonymous account is used, it is then returned.
		//		Default: false
		//	2.	$guest: Whether or not this user is a guest (no permissions).
		//		Default: true
		//	3.	$instance: The instance to update.
		//		Default: false
		//	4.	$color: An array containing the red, green, and blue values (from 0 through 255) for the color of the user.
		//		Default: false
		//	5.	$permissions: An indexed array of what chat functions the user can use. If set to true, all permissions are enabled.
		//		Default: false
		//	6.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Updates a user without causing leave/join messages.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function update ($user, $guest = true, $instance = false, $color = false, $permissions = false, $is_input = true)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						if (
							is_null ($user)
						)
							{
								// Reset the error handler as to not interfere with code inheriting this object.
								molten_chat_common :: restore_handlers ();

								return false;
							}

						// Cast $instance to a type we accept.
						$instance = is_array ($instance) ?
							$instance :
							(is_integer ($instance) ?
								array (
									'identifier' => $instance,
									'instance' => 'default'
								) :
								false);

						$instance = (
							isset ($instance ['identifier']) and
							isset ($instance ['instance'])
						) ?
							array (
								'identifier' => self :: to_string ($instance ['identifier']),
								'instance' => self :: to_string ($instance ['instance'])
							) :
							false;

						// Cast $color to a type we accept.
						$color = is_array ($color) ?
							((
								isset ($color ['blue']) and
								isset ($color ['green']) and
								isset ($color ['red'])
							) ?
								((
									is_integer ($color ['blue']) and
									is_integer ($color ['green']) and
									is_integer ($color ['red'])
								) ?
									$color :
									false) :
								false) :
							(boolean) $color;

						// Cast $permissions to a type we accept.
						$permissions = is_array ($permissions) ?
							$permissions :
							(boolean) $permissions;

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						$user = $is_input ?
							self :: clean_data ($user) :
							$user;

						// Lock users.
						molten_chat_cache :: lock_users (true);

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;
						$i_success = &$success;

						// Get the current users in the room.
						$users = is_array ($users = molten_chat_cache :: room_memory_get ('users')) ?
							$users :
							array ('guests' => array (), 'instances' => array (), 'users' => array ('index' => array (), 'data' => array ()));

						if (
							$guest
						)
							{
								if (
									!empty ($users ['guest'] [$user])
								)
									{
										//$users ['guest'] [$user] = array ();

										$success = true;
									}
							}
						else
							{
								$index = array_search ($user, $users ['users'] ['index']);

								if (
									$index !== false
								)
									{
										$difference = (time () - $users ['users'] ['data'] [$index] ['time']);

										$users ['users'] ['data'] [$index] ['time'] = time ();

										// Update the color.
										if (
											is_array ($color)
										)
											{
												$users ['users'] ['data'] [$index] ['color'] = $color;
											}

										// Remove the color.
										if (
											$color === true
										)
											{
												$users ['users'] ['data'] [$index] ['color'] = false;
											}

										// Check if we're updating an instance.
										if (
											$instance !== false
										)
											{
												// Change $i_success to a different value than what $success is.
												unset ($i_success);
												$i_success = false;

												// Lock memory.
												molten_chat_cache :: lock_memory (true);

												// Get the current instances.
												$instances = is_array ($instances = molten_chat_cache :: global_memory_get ('instances')) ?
													$instances :
													array ();

												if (
													isset ($instances [$instance ['identifier']] [(integer) $instance ['instance']])
												)
													{
														$instances [$instance ['identifier']] [(integer) $instance ['instance']] = time ();

														molten_chat_cache :: global_memory_set ('instances', $instances);

														$i_success = true;
													}

												// Release memory lock.
												molten_chat_cache :: lock_memory (false);
											}

										$success = true;
									}
							}

						if (
							$success
						)
							{
								// Set the current users in the room.
								molten_chat_cache :: room_memory_set ('users', $users);

								$action_time = is_integer ($action_time = molten_chat_cache :: room_memory_get ('user_time')) ?
									$action_time :
									0;

								if (
									$action_time < (time () - self :: $update) or
									$difference > self :: $update
								)
									{
										// Increment user action count by one.
										$action = is_integer ($action = molten_chat_cache :: room_memory_get ('user')) ?
											$action :
											0;
										molten_chat_cache :: room_memory_set ('user', ++$action);
										molten_chat_cache :: room_memory_set ('user_time', time ());
									}
							}

						// Release users lock.
						molten_chat_cache :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						return $i_success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: leave
		// Title: Leave a room.
		// Variables: $user, $guest, $is_input
		//	1.	$user: The user that has left the room, if this is left blank an anonymous account is used.
		//	2.	$guest: If the user is a guest or not.
		//		Default: true
		//	3.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		// Description: Removes a user from the room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function leave ($user, $guest = true, $is_input = true)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							self :: to_string ($user);

						// Check if the user is null.
						if (
							is_null ($user)
						)
							{
								// Reset the error handler as to not interfere with code inheriting this object.
								molten_chat_common :: restore_handlers ();

								return false;
							}

						// Clean other input by typecasting it.
						$guest = (boolean) $guest;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}
					
				if (
					self :: $room ['data'] ['active']
				)
					{
						// Lock users.
						molten_chat_cache :: lock_users (true);

						$users = molten_chat_cache :: room_memory_get ('users');

						// We have to run all the way to the end of this function so we use this simple switch.
						$success = false;

						if (
							$guest
						)
							{
								if (
									!empty ($users ['guest'] [$user])
								)
									{
										// Remove guest from list, and reorganize count.
										//array_splice ($users ['guest'], ($user + 1), 1);

										$success = true;
									}
							}
						else
							{
								$index = array_search ($user, $users ['users'] ['index']);

								if (
									$index !== false
								)
									{
										// Remove user from index.
										array_splice ($users ['users'] ['index'], $index, 1);

										// Remove user from list.
										array_splice ($users ['users'] ['data'], $index, 1);

										$success = true;
									}
							}

						if (
							$success
						)
							{
								// Set the current users in the room.
								molten_chat_cache :: room_memory_set ('users', $users);

								// Increment user action count by one.
								$action = is_integer ($action = molten_chat_cache :: room_memory_get ('user')) ?
									$action :
									0;
								molten_chat_cache :: room_memory_set ('user', ++$action);
								molten_chat_cache :: room_memory_set ('user_time', time ());
							}

						// Release users lock.
						molten_chat_cache :: lock_users (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						return $success;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: send
		// Title: Send a message.
		// Variables: $message, $user, $is_input, $call_function
		//	1.	$message: The message to send to the room.
		//	2.	$user: The user the message is sent as. If left false the message is sent as a system message.
		//		Default: false
		//	3.	$is_input: Whether or not this is unfiltered input.
		//		Default: true
		//	4.	$call_function: Whether or not to call a function if the message starts with a forward slash.
		//		Default: true
		// Description: Sends a message to a chat room.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function send ($message, $user = null, $is_input = true, $call_function = true, $link = false)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Make sure this variable is a boolean.
						$is_input = (boolean) $is_input;

						// Clean the message.
						$message = $is_input ?
							self :: clean_data ($message) :
							$message;

						// Typecast the message to string.
						$message = self :: to_string ($message);

						// Cast $user to a type we accept.
						$user = is_string ($user) ?
							($is_input ?
								self :: clean_data ($user) :
								$user) :
							!is_null ($user) ?
								self :: to_string ($user) :
								false;

						// Check if the user is null.
						$user = !is_null ($user) ?
							$user :
							false;

						// Cast $link to the proper type.
						$link = (
							is_array ($link) and
							isset ($link ['uri'])
						) ?
							array (
								'local' => !empty ($link ['local']),
								'uri' => $link ['uri']
							) :
							false;

						if (
							$link
						)
							{
								$link ['uri'] = is_string ($link ['uri']) ?
									($is_input ?
										self :: clean_data ($link ['uri']) :
										$link ['uri']) :
									self :: to_string ($link ['uri']);

								$link ['uri'] = !is_null ($link ['uri']) ?
									$link ['uri'] :
									false;
							}

						// Clean other input by typecasting it.
						$call_function = (boolean) $call_function;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				// We don't replace spaces in the beginning because someone may have used the space to prevent a function call from occuring.
				$message = preg_replace (array ('/\\s{2,}/', '/\\s*$/'), array (' ', ''), $message);

				if (
					$message != '' and
					self :: $room ['data'] ['active']
				)
					{
						// Lock room.
						molten_chat_cache :: lock_room (true);

						// Check if the message starts with a slash; indicating a command.
						if (
							$call_function and
							$message [0] == '/'
						)
							{
								// Since we don't know if this function should appear in the log or not, we just rely on the result.
								if (
									self :: call_function ($user, $message)
								)
									{
										// Release room lock.
										molten_chat_cache :: lock_room (false);

										// Reset the error handler as to not interfere with code inheriting this object.
										molten_chat_common :: restore_handlers ();

										return true;
									}
							}

						// Now we strip spaces from the start.
						$message = preg_replace ('/^\\s*/', '', $message);

						// Get the maximum amount of messages allowed per level.
						$limit = self :: $room ['data'] ['messages'];

						// Get the current level and message number.
						$current = !is_null ($current = molten_chat_cache :: room_memory_get ('current')) ?
							$current :
							self :: get_current ();
						$level = &$current ['level'];
						$line = &$current ['line'];

						if (
							$line >= 0 and
							$line < $limit
						)
							{
								// Get the current messages of the log.
								// Lots of memory usage here if you have large level sizes.
								$message_log = molten_chat_cache :: room_memory_get ('message_log');
							}

						// Export current level to a file.
						if (
							$line == 0
						)
							{
								$log_file = self :: $directory . self :: $room ['data'] ['base'] . '.' . ($level - 1) . self :: $extension;

								$input = array (
									self :: $room ['data'] ['name'],
									date ('l, F jS, Y', self :: $room ['data'] ['created']),
									date ('H:i:s', self :: $room ['data'] ['created']),
									((self :: $room ['data'] ['owner'] !== false) ?
										self :: $room ['data'] ['owner'] :
										self :: $language ['system'])
								);

								// Grab header contents to append.
								$header = (self :: $room ['data'] ['owner'] !== false) ?
									vsprintf (self :: $language ['log_owned'], $input) :
									vsprintf (self :: $language ['log_system'], $input);

								$input = array (
									0,
									self :: $room ['data'] ['created'],
									((self :: $room ['data'] ['owner'] !== false) ?
										self :: parse_data (self :: $room ['data'] ['owner']) :
										'false'),
									self :: parse_data ($header),
									var_export ($message_log, true)
								);

								$output = vsprintf (self :: $formatting ['log'], $input) . "\n";

								// Open up the log.
								$pointer = fopen ($log_file, 'a+');

								// Acquire lock without blocking.
								while (
									!flock ($pointer, LOCK_EX | LOCK_NB)
								)
									{
										// Generate a random number.
										$random = mt_rand (0, 250);

										// Sleep for 0.000 to 0.250 seconds. seconds.
										usleep (1000 * $random);
									}

								// Empty file.
								ftruncate ($pointer, 0);

								// Write message log to file.
								fwrite ($pointer, $output);

								// Close log file.
								flock ($pointer, LOCK_UN);
								fclose ($pointer);

								// Check for connection.
								if (
									molten_chat_database :: database_connect ()
								)
									{
										// Update database.
										$sql = '';

										molten_chat_database :: query ($sql, false);
									}
								
								// Reset message log array.
								$message_log = array ();
							}

						// Append message to log.
						$message_log [] = array (
							'line' => $line,
							'link' => $link,
							'time' => time (),
							'user' => $user,
							'message' => $message
						);

						// Write to memory.
						molten_chat_cache :: room_memory_set ('message_log', $message_log);

						// Finally, increment the message count.
						$current = (($line + 1) < self :: $room ['data'] ['messages']) ?
							array (
								'level' => $level,
								'line' => ($line + 1)
							) :
							array (
								'level' => ($level + 1),
								'line' => 0
							);

						// We do this last to prevent other threads from trying to read the new messages before they're written.
						molten_chat_cache :: room_memory_set ('current', $current);

						// Release room lock.
						molten_chat_cache :: lock_room (false);

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						return true;
					}

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: push
		// Title: Invoke AJAX Push.
		// Variables: $level, $line, $action, $limit, $speed
		//	1.	$level: The current message log level of the instance.
		//	2.	$line: The current line of the instance.
		//	3.	$action: The current action identifier for user control.
		//	4.	$instance: The current instance.
		//		Default: false
		//	5.	$limit: The maximum time (in seconds) to hold out for before returning false.
		//		Default: 30
		//	6.	$speed: The rate at which the system checks for new messages (in milliseconds). Higher is slower, but less resource-intensive.
		//		Default: 250
		//	7.	$return: Whether or not to return the XML document generated upon error.
		//		Default: false
		// Description: Delays the result of the transaction for a certain time until there is new information available.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function push ($level, $line, $action, $instance = false, $limit = 30, $speed = 250, $return = false)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				$time = time ();
				$start = microtime (true) - $time;
				self :: $comments [] = 'push: started at ' . ($start + $time) . '.';

				try
					{
						$instance = (self :: $instances > 0) ?
							$instance :
							false;

						// Cast $instance to a type we accept.
						$instance = is_array ($instance) ?
							$instance :
							(is_integer ($instance) ?
								array (
									'identifier' => $instance,
									'instance' => 'default'
								) :
								false);

						$instance = (
							isset ($instance ['identifier']) and
							isset ($instance ['instance'])
						) ?
							array (
								'identifier' => self :: to_string ($instance ['identifier']),
								'instance' => self :: to_string ($instance ['instance'])
							) :
							false;

						// Clean other input by typecasting it.
						$level = (integer) $level;
						$line = (integer) $line;
						$action = (integer) $action;
						$limit = (integer) $limit;
						$speed = (integer) $speed;
					}
				catch (Exception $exception)
					{
						$end = microtime (true) - $time;
						$delay = $end - $start;
						self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
						self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						set_time_limit ($limit + 30);

						// Speed should not be more than one-half of the time limit.
						$speed = $speed <= ($limit * 500) ?
							$speed :
							($limit * 500);

						$limit += time ();

						// Speed is actually in microseconds. So we must multiply it by a thousand to convert milliseconds to microseconds.
						$speed *= 1000;

						self :: $room ['temporary'] ['level'] = $level;
						self :: $room ['temporary'] ['current'] = $line;
						self :: $room ['temporary'] ['user'] = $action;

						do
							{
								// Check if the instance has expired.
								if (
									$instance !== false
								)
									{
										$output = self :: check_instances ($instance, $return);

										if (
											$output
										)
											{
												$end = microtime (true) - $time;
												$delay = $end - $start;
												self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
												self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

												// Reset the error handler as to not interfere with code inheriting this object.
												molten_chat_common :: restore_handlers ();

												if (
													is_bool ($output)
												)
													{
														return false;
													}
												else
													{
														return $output;
													}
											}
									}

								// Zzzzz... We do this first to prevent request spam.
								usleep ($speed);

								// Check if anything has changed.
								if (
									self :: check_logs () or
									self :: check_users () or
									(
										$level == 0 and
										$line == 0
									)
								)
									{
										$end = microtime (true) - $time;
										$delay = $end - $start;
										self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
										self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

										// Reset the error handler as to not interfere with code inheriting this object.
										molten_chat_common :: restore_handlers ();

										return true;
									}
							}
						// Check if we still have time to do it again!
						while (
							time () < $limit
						);
					}

				$end = microtime (true) - $time;
				$delay = $end - $start;
				self :: $comments [] = 'push: stopped at ' . ($end + $time) . '.';
				self :: $comments [] = 'push: delayed execution for ' . $delay . ' second' . (($delay < 1 or $delay > 1) ? 's' : '') . '.';

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				// Aww, nothing changed. Sad face. :(
				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: retrieve
		// Title: Retrieve room data.
		// Variables: $level, $line, $action, $return
		//	1.	$level: The current message log level of the instance.
		//	2.	$line: The current line of the instance.
		//	3.	$action: The current action identifier for user control.
		//	4.	$instance: The current instance.
		//		Default: false
		//	5.	$return: Whether or not to return the output instead of echo it.
		//		Default: false
		// Description: Retrieve up to date room information; messages, users, etc.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function retrieve ($level, $line, $action, $instance = false, $return = false)
			{
				// We rely on this error handler.
				molten_chat_common :: set_handlers ();

				try
					{
						// Cast $instance to a type we accept.
						$instance = ($instance !== false) ?
							self :: to_string ($instance) :
							false;

						// Clean other input by typecasting it.
						$level = (integer) $level;
						$line = (integer) $line;
						$action = (integer) $action;
						$return = (boolean) $return;
					}
				catch (Exception $exception)
					{
						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						// Error, we can take input that is not standard, but not kinds that we can't convert.
						return false;
					}

				if (
					self :: $room ['data'] ['active']
				)
					{
						// Declare initial variables.
						$actions = null;
						$document = null;
						$levels = 0;
						$messages = null;
						$message_log = array ();
						$input = array ();
						$output = false;
						$users = null;

						self :: $room ['temporary'] ['level'] = $level;
						self :: $room ['temporary'] ['current'] = $line;
						self :: $room ['temporary'] ['user'] = $action;

						if (
							$instance !== false
						)
							{
								// Create XML document.
								$document = new DOMDocument ('1.0', 'utf-8');

								// Create chat body.
								$moltx_chat = $document -> createElement ('moltx_chat');

								$input = array_merge ($input, array (
									'instance' => $instance
								));
							}

						if (
							self :: check_logs () or
							(
								$level == 0 and
								$line == 0
							)
						)
							{
								// Get the current level and message number.
								//$level = molten_chat_cache :: room_memory_get ('level');
								//$line = molten_chat_cache :: room_memory_get ('current');
								$current = !is_null ($current = molten_chat_cache :: room_memory_get ('current')) ?
									$current :
									self :: get_current ();
								$level = $current ['level'];
								$line = $current ['line'];

								// Check if we need to load any previous logs.
								if (
									$level > 0 and
									(
										self :: $room ['temporary'] ['level'] < ($level - 1) or
										(
											self :: $room ['temporary'] ['level'] == 0 and
											self :: $room ['temporary'] ['current'] == 0
										) or
										(
											self :: $room ['temporary'] ['level'] < $level and
											$line > 0
										)
									)
								)
									{
										if (
											self :: $room ['temporary'] ['level'] < $level and
											(
												self :: $room ['temporary'] ['level'] != 0 or
												self :: $room ['temporary'] ['current'] != 0
											)
										)
											{
												// Get the amount of levels needed to be loaded to retrieve all the messages necesarry to populate the log.
												$levels = $level - self :: $room ['temporary'] ['level'];
											}
										else
											{
												// Get the amount of levels needed to be loaded to retrieve all the messages necesarry to populate the log.
												$levels = ceil (self :: $room ['data'] ['retrieve'] / self :: $room ['data'] ['messages']);
												$excess = ($level - $levels);
												$levels = ($excess >= 0) ?
													$levels :
													($levels + $excess);
												$levels = $levels > self :: $room ['data'] ['levels'] ?
													self :: $room ['data'] ['levels'] :
													$levels;
											}

										// Whoo backwards.
										for ($i = $levels, $minimum = (($line > 0) ? 0 : 1); $i > $minimum; --$i)
											{
												// Get previous logs.
												try
													{
														include (self :: $directory . self :: $room ['data'] ['base'] . '.' . ($level - $i) . self :: $extension);
													}
												catch (Exception $exception)
													{
														// Add "we are missing messages" statement to the log.
														$last = end ($message_log);
														if (
															$last ['message'] != self :: $language ['not_contiguous_log']
														)
															{
																$message_log [] = array (
																	'message' => self :: $language ['not_contiguous_log']
																);
															}
													}
											}
									}

								// Get the current level's messages from memory.
								$current_log = molten_chat_cache :: room_memory_get ('message_log');

								// Check if we even successfully got messages from memory.
								if (
									is_array ($current_log)
								)
									{
										// Merge it with the existing logs... This is if we loaded older logs above.
										$message_log = array_merge ($message_log, $current_log);
									}
								else
									{
										try
											{
												// Grab the current log from disk.
												include (self :: $directory . self :: $room ['data'] ['base'] . '.' . $level . self :: $extension);
											}
										catch (Exception $exception)
											{
												// Whatev'.
											}
									}
								
								++$levels;

								// Log for output.
								self :: $comments [] = 'retrieve: loaded ' . $levels . ' level' . (($levels > 1 or $levels == 0) ? 's' : '') . '.';

								// Get the total number of messages in the log.
								$total = count ($message_log);

								// This would be a little too confusing if I tried to combine it all into one ternary statement.
								if (
									(
										self :: $room ['temporary'] ['level'] != 0 or
										self :: $room ['temporary'] ['current'] != 0
									) and
									(
										self :: $room ['temporary'] ['level'] < ($level - 1) or
										(
											self :: $room ['temporary'] ['level'] < $level and
											self :: $room ['temporary'] ['current'] < self :: $room ['data'] ['messages']
										)
									)
								)
									{
										$start = self :: $room ['temporary'] ['current'];
									}
								elseif (
									self :: $room ['temporary'] ['level'] == 0 and
									self :: $room ['temporary'] ['current'] == 0
								)
									{
										$start = (self :: $room ['data'] ['retrieve'] <= $total) ?
											((($start = $total - self :: $room ['data'] ['retrieve']) >= 0) ?
												$start :
												0) :
											0;
									}
								else
									{
										$start = $total - ($total - self :: $room ['temporary'] ['current']);
									}

								if (
									!isset ($users_array)
								)
									{
										// Load users.
										$users_array = is_array ($users_array = molten_chat_cache :: room_memory_get ('users')) ?
											$users_array :
											array ('guests' => array (), 'instances' => array (), 'users' => array ('index' => array (), 'data' => array ()));
									}

								if (
									!($document instanceof DOMDocument)
								)
									{
										// Create XML document.
										$document = new DOMDocument ('1.0', 'utf-8');

										// Create chat body.
										$moltx_chat = $document -> createElement ('moltx_chat');
									}

								$input = array_merge ($input, array (
									'level' => $level,
									'line' => $line,
									'limit' => self :: $room ['data'] ['messages']
								));

								if (
											self :: $room ['temporary'] ['level'] == 0 and
											self :: $room ['temporary'] ['current'] == 0
								)
									{
										try
											{
												if (
													is_array (self :: $room ['data'] ['announcement'])
												)
													{
														$length = (self :: $room ['temporary'] ['current'] >= self :: $room ['data'] ['retrieve']) ?
															3 :
															2;

														// Add messages to the front of the array.
														array_splice ($message_log, $start, $length, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));

														$message_log [] = self :: $room ['data'] ['announcement'];

														if (
															self :: $room ['data'] ['retrieve'] == 2
														)
															{
																// Increment total.
																++$total;
															}

														if (
															$length == 2
														)
															{
																// Increment total.
																++$total;
															}
													}
												else
													{
														// Add messages to the front of the array.
														array_splice ($message_log, $start, 2, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));
													}
											}
										catch (Exception $exception)
											{
												// Add messages to the front of the array.
												array_splice ($message_log, $start, 2, array (array ('message' => self :: $language ['room_connected']), array ('message' => self :: $room ['data'] ['header'])));
											}

										// Create actions root element.
										$actions = $document -> createElement ('actions');

										// Create "connected" action.
										$action = $document -> createElement ('action');
										$attribute = $document -> createAttribute ('type');
										$value = $document -> createTextNode ('connect');
										$attribute -> appendChild ($value);
										$action -> appendChild ($attribute);
										$actions -> appendChild ($action);
									}

								// Create messages root element.
								$messages = $document -> createElement ('messages');

								// Make XML.
								for ($i = $start; $i < $total; ++$i)
									{
										// Create a new message.
										$message = $document -> createElement ('message');

										// Check if there is a line associated with this message.
										if (
											isset ($message_log [$i] ['line'])
										)
											{
												$attribute = $document -> createAttribute ('line');
												$value = $document -> createTextNode ((string) $message_log [$i] ['line']);
												$attribute -> appendChild ($value);
												$message -> appendChild ($attribute);
											}

										// Check if there is a link associated with this message.
										if (
											!empty ($message_log [$i] ['link'])
										)
											{
												$attribute = $document -> createAttribute ('link');

												// Check if this link should be run through the link handling callback.
												$value = empty ($message_log [$i] ['link'] ['local']) ?
													$document -> createTextNode ((string) $message_log [$i] ['link'] ['uri']) :
													$document -> createTextNode (molten_chat_module :: parse_link ((string) $message_log [$i] ['link'] ['uri']));

												$attribute -> appendChild ($value);
												$message -> appendChild ($attribute);
											}

										// Check if there is a time associated with this message.
										if (
											isset ($message_log [$i] ['time'])
										)
											{
												$attribute = $document -> createAttribute ('time');
												$value = $document -> createTextNode ((string) $message_log [$i] ['time']);
												$attribute -> appendChild ($value);
												$message -> appendChild ($attribute);
											}

										// Check if there is a user associated with this message.
										if (
											isset ($message_log [$i] ['user'])
										)
											{
												if (
													$message_log [$i] ['user'] != ''
												)
													{
														// Get the location of the user in the data array.
														$index = array_search ($message_log [$i] ['user'], $users_array ['users'] ['index'], true);

														// Create user element.
														$user = $document -> createElement ('by');

														// Check if the user is in the data array.
														if (
															$index !== false
														)
															{
																// Check if there is a color associated with this user.
																if (
																	$users_array ['users'] ['data'] [$index] ['color'] !== false
																)
																	{
																		$color = &$users_array ['users'] ['data'] [$index] ['color'];

																		$attribute = $document -> createAttribute ('color');
																		$value = $document -> createTextNode ($color ['red'] . ',' . $color ['green'] . ',' . $color ['blue']);
																		$attribute -> appendChild ($value);
																		$user -> appendChild ($attribute);
																	}
															}
														else
															{
																molten_chat_module :: handle_user ((string) $message_log [$i] ['user']);
															}

														// Add user text to element.
														$text = $document -> createTextNode ((string) $message_log [$i] ['user']);
														$user -> appendChild ($text);

														// Add user element to message.
														$message -> appendChild ($user);
													}
											}

										// Add message body to message element.
										$content = $document -> createElement ('content');
										$text = $document -> createTextNode ((string) $message_log [$i] ['message']);
										$content -> appendChild ($text);
										$message -> appendChild ($content);

										// Add message to messages root element.
										$messages -> appendChild ($message);
									}
							}

						if (
							self :: check_users ()
						)
							{
								if (
									!isset ($users_array)
								)
									{
										// Load users.
										$users_array = is_array ($users_array = molten_chat_cache :: room_memory_get ('users')) ?
											$users_array :
											array ('guests' => array (), 'instances' => array (), 'users' => array ('index' => array (), 'data' => array ()));
									}

								// Check if the DOMDocument object was already instantiated.
								if (
									!($document instanceof DOMDocument)
								)
									{
										// Create XML document.
										$document = new DOMDocument ('1.0', 'utf-8');

										// Create chat body.
										$moltx_chat = $document -> createElement ('moltx_chat');
									}

								// Get the current action number.
								$action_number = is_integer ($action_number = molten_chat_cache :: room_memory_get ('user')) ?
									$action_number :
									0;

								$input = array_merge ($input, array (
									'action' => $action_number,
									'guests' => count ($users_array ['guests']),
									'users' => count ($users_array ['users'] ['index'])
								));

								// Create users root element.
								$users = $document -> createElement ('users');

								// Make XML.
								for ($i = 0, $total = count ($users_array ['users'] ['data']); $i < $total; ++$i)
									{
										// Create a new user.
										$user = $document -> createElement ('user');

										// Check if there is a color associated with this user.
										if (
											$users_array ['users'] ['data'] [$i] ['color'] !== false
										)
											{
												$color = &$users_array ['users'] ['data'] [$i] ['color'];

												$attribute = $document -> createAttribute ('color');
												$value = $document -> createTextNode ($color ['red'] . ',' . $color ['green'] . ',' . $color ['blue']);
												$attribute -> appendChild ($value);
												$user -> appendChild ($attribute);
											}

										// Check if there is a time associated with this user.
										if (
											isset ($users_array ['users'] ['data'] [$i] ['time'])
										)
											{
												$attribute = $document -> createAttribute ('time');
												$value = $document -> createTextNode ((string) $users_array ['users'] ['data'] [$i] ['time']);
												$attribute -> appendChild ($value);
												$user -> appendChild ($attribute);
											}

										// Add user body to user element.
										$text = $document -> createTextNode ((string) $users_array ['users'] ['data'] [$i] ['name']);
										$user -> appendChild ($text);

										// Add user to users root element.
										$users -> appendChild ($user);
									}
							}

						if (
							$document instanceof DOMDocument
						)
							{
								// Add comments to document.
								self :: add_comments ($document);
								
								$input = array_merge ($input, array (
									'time' => time ()
								));

								ksort ($input);

								foreach ($input as $key => $set)
									{
										// Add data element.
										$data = $document -> createElement ('data');
										$attribute = $document -> createAttribute ('type');
										$value = $document -> createTextNode ((string) $key);
										$attribute -> appendChild ($value);
										$data -> appendChild ($attribute);
										$attribute = $document -> createAttribute ('value');
										$value = $document -> createTextNode ((string) $set);
										$attribute -> appendChild ($value);
										$data -> appendChild ($attribute);

										// Add data element to root element.
										$moltx_chat -> appendChild ($data);
									}

								if (
									$actions instanceof DOMElement
								)
									{
										// Add actions element to root element.
										$moltx_chat -> appendChild ($actions);
									}

								if (
									$messages instanceof DOMElement
								)
									{
										// Add messages root element to root element.
										$moltx_chat -> appendChild ($messages);
									}

								if (
									$users instanceof DOMElement
								)
									{
										// Add users root element to root element.
										$moltx_chat -> appendChild ($users);
									}

								// Append root element to document.
								$document -> appendChild ($moltx_chat);

								// Set document type to XML.
								header ('Content-Type: text/xml');

								// Fancy output equals yes!
								$document -> formatOutput = true;

								// Parse document into a string.
								$output = $document -> saveXML ();
							}

						if (
							$output !== false
						)
							{
								// Reset the error handler as to not interfere with code inheriting this object.
								molten_chat_common :: restore_handlers ();

								if (
									!$return
								)
									{
										// Print the output.
										echo $output;

										return true;
									}
								else
									{
										return $output;
									}
							}
					}
				else
					{
						// Create XML document.
						$document = new DOMDocument ('1.0', 'utf-8');

						// Add comments to document.
						self :: add_comments ($document);

						// Create chat body.
						$moltx_chat = $document -> createElement ('moltx_chat');

						// Create actions root element.
						$actions = $document -> createElement ('actions');

						$input = array (
							'connect' => (string) self :: $language ['room_not_exist'],
							'disconnect' => (string) self :: $language ['room_disconnected']
						);

						// Create "this room does not exist" message.
						// Create "disconnected" message."
						foreach ($input as $key => $set)
							{
								$action = $document -> createElement ('action');
								$attribute = $document -> createAttribute ('type');
								$value = $document -> createTextNode ($key);
								$attribute -> appendChild ($value);
								$action -> appendChild ($attribute);
								$text = $document -> createTextNode ($set);
								$action -> appendChild ($text);

								// Append action to actions root.
								$actions -> appendChild ($action);
							}

						// Append actions root element to root element.
						$moltx_chat -> appendChild ($actions);

						// Append root element to document.
						$document -> appendChild ($moltx_chat);

						// Set document type to XML.
						header ('Content-Type: text/xml');

						// Fancy output equals yes!
						$document -> formatOutput = true;

						// Parse document into a string.
						$output = $document -> saveXML ();

						// Reset the error handler as to not interfere with code inheriting this object.
						molten_chat_common :: restore_handlers ();

						if (
							!$return
						)
							{
								// Print the output.
								echo $output;

								return true;
							}
						else
							{
								return $output;
							}
					}

				// Create XML document.
				$document = new DOMDocument ('1.0', 'utf-8');
				
				self :: add_comments ($document);

				// Create chat body.
				$moltx_chat = $document -> createElement ('moltx_chat');

				// Append root element to document.
				$document -> appendChild ($moltx_chat);

				// Set document type to XML.
				header ('Content-Type: text/xml');

				// Fancy output equals yes!
				$document -> formatOutput = true;

				// Parse document into a string.
				$output = $document -> saveXML ();

				// Reset the error handler as to not interfere with code inheriting this object.
				molten_chat_common :: restore_handlers ();

				if (
					!$return
				)
					{
						// Print the output.
						echo $output;

						return true;
					}
				else
					{
						return $output;
					}
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_instances
		// Title: Check for instance changes.
		// Variables: $instance, $return
		//	1.	$instance: A reference to the instance array.
		//	2.	$return: Whether or not to return the output.
		//		Default: false
		// Description: Checks if the message log changed since the last transaction.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_instances (&$instance, $return = false)
			{
				// Get the current instances.
				$instances = molten_chat_cache :: global_memory_get ('instances');

				if (
					is_null ($instances)
				)
					{
						$instance = false;
						return false;
					}

				$instances = $instances [$instance ['identifier']];

				arsort ($instances);

				// Cut off all instances that should be closed.
				$instances = array_slice ($instances, 0, self :: $instances, true);

				if (
					!isset ($instances [$instance ['instance']])
				)
					{
						// Output XML.

						// Create XML document.
						$document = new DOMDocument ('1.0', 'utf-8');

						// Add comments to document.
						self :: add_comments ($document);

						// Create chat body.
						$moltx_chat = $document -> createElement ('moltx_chat');

						// Create actions root element.
						$actions = $document -> createElement ('actions');

						$input = array (
							'connect' => (string) self :: $language ['too_many_instances'],
							'disconnect' => (string) self :: $language ['room_disconnected']
						);

						// Create "this room does not exist" message.
						// Create "disconnected" message."
						foreach ($input as $key => $set)
							{
								$action = $document -> createElement ('action');
								$attribute = $document -> createAttribute ('type');
								$value = $document -> createTextNode ($key);
								$attribute -> appendChild ($value);
								$action -> appendChild ($attribute);
								$text = $document -> createTextNode ($set);
								$action -> appendChild ($text);

								// Append action to actions root.
								$actions -> appendChild ($action);
							}

						// Append actions root element to root element.
						$moltx_chat -> appendChild ($actions);

						// Append root element to document.
						$document -> appendChild ($moltx_chat);

						// Set document type to XML.
						header ('Content-Type: text/xml');

						// Fancy output equals yes!
						$document -> formatOutput = true;

						// Parse document into a string.
						$output = $document -> saveXML ();

						if (
							!$return
						)
							{
								// Print the output.
								echo $output;

								return true;
							}
						else
							{
								return $output;
							}
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_logs
		// Title: Check for log changes.
		// Variables: None.
		// Description: Checks if the message log changed since the last transaction.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_logs ()
			{
				// Get the current level and message number.
				//$level = molten_chat_cache :: room_memory_get ('level');
				//$current = molten_chat_cache :: room_memory_get ('current');
				$current = is_array ($current = molten_chat_cache :: room_memory_get ('current')) ?
					$current :
					array ('level' => 0, 'line' => 0);
				$level = &$current ['level'];
				$line = &$current ['line'];

				if (
					$level > self :: $room ['temporary'] ['level'] or
					$line > self :: $room ['temporary'] ['current']
				)
					{
						return true;
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_users
		// Title: Check for user changes.
		// Variables: None.
		// Description: Checks if the user list changed since the last transaction.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_users ()
			{
				// Get the current action number.
				$action = is_integer ($action = molten_chat_cache :: room_memory_get ('user')) ?
					$action :
					0;

				if (
					$action > self :: $room ['temporary'] ['user']
				)
					{
						// Update the internal value.
						//self :: $room ['temporary'] ['user'] = $user;

						return true;
					}

				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: check_function
		// Title: Check a function.
		// Variables: $user, $function
		//	1.	$user: The user the message is sent as.
		//	2.	$function: The function name.
		// Description: Check if a user is permitted to use a function.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function check_function ($user, $function)
			{
				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: call_function
		// Title: Call a function.
		// Variables: $user, $function, $override
		//	1.	$user: The user the message is sent as.
		//	2.	$function: The function name.
		//	3.	$override: Whether or not to override the user's permissions.
		//		Default: false
		// Description: Calls a function if the user is permitted to do so.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function call_function ($user, $message, $override = false)
			{
				$arguments = array ();
				$function = false;
				$matches = array ();
				$method = '';

				$regex = array (
					'global' => '/(?<full>^\\/[a-zA-Z_][0-9a-zA-Z_]+(?:(?::|,).*)?|[a-zA-Z_][0-9a-zA-Z_]+(?:\\s*\\=\\s*(?:(?:\\s*,\\s*|(?<=\\=))(?:true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\2))+)?)/i',
					'arguments' => '/(?<key>^[a-zA-Z_][0-9a-zA-Z_]*)(?:\\s*\\=\\s*(?<value>(?:(?:\\s*,\\s*|(?<=\\=))(?:true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\3))+))?/i',
					'values' => '/(?:(?:\\s*,\\s*|(?<=^)\\s*)(?<values>true|false|null|[0-9]+|[0-9]*\\.[0-9]+|(["\'])(?:\\\\?+.)*?\\2))/i',
					'values_structured' => '/(?<full>:(?:(?:\\\\\\\\)*\\\\[:,]|[^\\\\:,]|\\\\\\\\|\\\\(?![:,]))*|,.*)/i'
				);

				preg_match_all ($regex ['global'], $message, $matches);

				$index = array ();

				// Check if we are using the structured syntax instead of the .
				$index [] = strpos ($matches ['full'] [0], ':');
				$index [] = strpos ($matches ['full'] [0], ',');
				$index = (
					$index [0] !== false and
					(
						$index [0] < $index [1] or
						$index [1] === false
					)
				) ?
					$index [0] :
					$index [1];

				if (
					$index !== false
				)
					{
						$structured = substr ($matches ['full'] [0], $index);

						$values = array ();

						preg_match_all ($regex ['values_structured'], $structured, $values);

						$items = array_merge (array (
							substr ($matches ['full'] [0], 0, $index)
						), $values ['full']);
					}
				else
					{
						$items = &$matches ['full'];
					}

				foreach ($items as $match)
					{
						if (
							!empty ($match)
						)
							{
								switch ($match [0])
									{
										case ':':
											$match = self :: strip_escaping ($match);

										case ',':
											// Strip the : or , off of the beginning.
											$value = substr ($match, 1);

											// Get rid of whitespace.
											$value = preg_replace (array ('/\\s{2,}/', '/(?:^\\s*|\\s*$)/'), array (' ', ''), $value);

											$arguments [] = $value;
											break;

										case '/':
											// It's the function.
											$function = substr ($match, 1);

											// Check if the function is permitted to be used.
											if (
												!$override and
												!self :: check_function ($user, $function)
											)
												{
													return false;
												}

											break;

										default:
											// It's an argument.
											$variable = array ();
											$values = array ();

											// Split all the arguments into one array containing the key, and the other containing the value.
											preg_match ($regex ['arguments'], $match, $variable);

											if (
												isset ($variable ['value'])
											)
												{
													// Split the values into an array if there is an array of values.
													preg_match_all ($regex ['values'], $variable ['value'], $values);

													if (
														($count = count ($values ['values'])) === 1
													)
														{
															// Guess the proper encoding of the contents of this string.
															$value = self :: typecast ($values ['values'] [0]);

															// Strip any user-added escaping if it is a string.
															$arguments [$variable ['key']] = self :: strip_escaping ($value);
														}
													elseif (
														$count >= 0
													)
														{
															// Guess the proper encoding of the contents of this string.
															$value = self :: typecast ($values ['values']);

															// Strip any user-added escaping if it is a string. This works on arrays.
															$arguments [$variable ['key']] = self :: strip_escaping ($value);
														}
													// This should not happen.
													else
														{
															$arguments [$variable ['key']] = true;
														}
												}
											else
												{
													$arguments [$variable ['key']] = true;
												}

											break;
									}
							}
					}

				// Get methods that exist.
				$methods = get_class_methods ('molten_chat_functions');

				// Check if the method can be called.
				if (
					$function != false and
					in_array ($function, $methods)
				)
					{
						try
							{
								// Variable functions rock!
								$result = molten_chat_functions :: $function ($user, $arguments);

								// True means don't send the function call, false means to send the function call.
								return $result;
							}
						catch (Exception $exception)
							{
								return false;
							}
					}
				
				return false;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: get_current
		// Title: Get current level and line from filesystem.
		// Variables: None.
		// Description: Guesses the current level and lines by using the filenames that exist for the room. Even if the level is higher than the level it guesses, the program will not break.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function get_current ()
			{
				self :: $comments [] = 'get_current: got level and line from disk.';

				// Create a regular expression to match all files that are log files.
				$regex = '/^' . preg_quote (self :: $room ['data'] ['base'], '/') . '\\.([0-9]+)\\.room\\.php$/';

				// Lock room.
				molten_chat_cache :: lock_room (true);

				// Get a listing of all files in the rooms directory.
				$files = scandir (self :: $directory);

				// Get all files that are legit log files.
				$matches = preg_grep ($regex, $files);

				// Get levels from matches.
				$levels = preg_replace ($regex, '\\1', $matches);

				// Typecast levels to integers.
				foreach ($levels as &$level)
					{
						$level = (integer) $level;
					}
				unset ($level);

				// Sort the levels.
				sort ($levels);

				// Get highest level.
				$level = (count ($levels) > 0) ?
					end ($levels) :
					0;

				try
					{
						include (self :: $directory . self :: $room ['data'] ['base'] . '.' . $level . self :: $extension);

						// Count lines.
						$line = (($line = count ($message_log)) < self :: $room ['data'] ['messages']) ?
							$line :
							0;

						// Set message log.
						molten_chat_cache :: room_memory_set ('message_log', $message_log);

						if (
							$line == 0
						)
							{
								// Increment level.
								++$level;
							}
					}
				catch (Exception $exception)
					{
						// It was deleted that fast? Wow!
						$line = 0;
					}

				$current = array (
					'level' => $level,
					'line' => $line
				);

				molten_chat_cache :: room_memory_set ('current', $current);

				// Release room lock.
				molten_chat_cache :: lock_room (false);

				return $current;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: add_comments
		// Title: Add comments.
		// Variables: $document
		//	1.	$document: The XML document to add comments to.
		// Description: Adds comments to a DOMDocument object.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function add_comments (&$document)
			{
				foreach (self :: $comments as $comment)
					{
						$comment = $document -> createComment (' ' . $comment . ' ');
						$document -> appendChild ($comment);
					}

				return true;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: strip_escaping
		// Title: Strip user-added escaping.
		// Variables: $data
		//	1.	$data: The data to be parsed.
		// Description: Strips user-added escaping to strings.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function strip_escaping ($data)
			{
				if (
					is_string ($data)
				)
					{
						// Get the type of quote used for this string.
						$type = $data [0];

						switch ($type)
							{
								case '\'':
									// Strip the apostrophes off the beginning and end of the string.
									$data = substr ($data, 1, -1);

									// Strip escaped slashes, and escaped quotes from this string.
									$data = str_replace (array ('\\\\', '\\\''), array ('\\', '\''), $data);
									break;
								case '"':
									// Strip the quotes off the beginning and end of the string.
									$data = substr ($data, 1, -1);

									// Strip escaped slashes, and escaped quotes from this string.
									$data = str_replace (array ('\\\\', '\\"'), array ('\\', '"'), $data);
									break;
								case ':':
									// Strip escaped slashes, and escaped quotes from this string.
									$data = str_replace (array ('\\\\', '\\:'), array ('\\', ':'), $data);
									break;
							}
					}
				// This function works with multi-dimensional arrays.
				elseif (
					is_array ($data)
				)
					{
						foreach ($data as &$value)
							{
								// Recursively call self.
								$value = self :: strip_escaping ($value);
							}
						unset ($value);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: typecast
		// Title: Cast variables to their proper type.
		// Variables: $data
		//	1.	$data: The data to be casted.
		// Description: Guesses the proper type of a string input by using various regular expressions.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function typecast ($data)
			{
				$regex = array (
					'boolean' => '/^(true|false)$/i',
					'null' => '/^null$/i',
					'integer' => '/^[0-9]+$/',
					'float' => '/^[0-9]*\\.[0-9]+$/',
					'string' => '(["\'])(?:\\\\?+.)*?\\1'
				);

				if (
					is_string ($data)
				)
					{
						switch (1)
							{
								// Check for a boolean.
								case preg_match ($regex ['boolean'], $data):
									$data = (!strcasecmp ($data, 'true')) ?
										true :
										false;
									break;

								// Check for a null.
								case preg_match ($regex ['null'], $data):
									$data = null;
									break;

								// Check for a integer.
								case preg_match ($regex ['integer'], $data):
									$data = (integer) $data;
									break;

								// Check for a float.
								case preg_match ($regex ['float'], $data):
									$data = (float) $data;
									break;
							}
					}
				// This function works with multi-dimensional arrays.
				elseif (
					is_array ($data)
				)
					{
						foreach ($data as &$value)
							{
								// Recursively call self.
								$value = self :: typecast ($value);
							}
						unset ($value);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: typecast
		// Title: Cast variables to their proper type.
		// Variables: $data
		//	1.	$data: The data to be casted.
		// Description: Guesses the proper type of a string input by using various regular expressions. Will flatten multi-dimensional arrays!
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		public static function to_string ($data)
			{
				if (
					!is_string ($data)
				)
					{
						switch (gettype ($data))
							{
								// Not much needs to take place here, just typecast it.
								case 'double':
								case 'float': // In case double changes.
								case 'integer':
									$data = (string) $data;
									break;

								// Convert booleans to "true" and "false" textual display.
								case 'boolean':
									$data = $data ?
										'true' :
										'false';
									break;

								// Convert an array into a comma-separated list, flattens multi-dimensional arrays!
								case 'array':
									// Create a new single-dimensional array.
									$value = array ();

									foreach ($data as $key => $value)
										{
											if (
												!is_string ($value)
											)
												{
													// Get the string value of the item.
													$value = self :: to_string ($value);

													// Check if it could not be converted; is only true for resources!
													if (
														!is_null ($value)
													)
														{
															if (
																is_integer ($key)
															)
																{
																	$values [] = $value;
																}
															else
																{
																	// Add element to new single-dimensional array.
																	$values [] = '\'' . self :: parse_data ($key) . '\'=' . $value;
																}
														}
												}
											else
												{
													if (
														is_integer ($key)
													)
														{
															$values [] = '\'' . self :: parse_data ($value) . '\'';
														}
													else
														{
															// Add element to new single-dimensional array.
															$values [] = '\'' . self :: parse_data ($key) . '\'=\'' . self :: parse_data ($value) . '\'';
														}
												}
										}

									// Implode array with commas as the separator.
									$data = implode (', ', $values);
									break;

								// Create a textual representation of the null value.
								case 'NULL':
									$data = 'null';
									break;

								// Convert an object to its textual representation.
								case 'object':
									// Get object methods.
									$methods = get_class_methods ($data);

									// Check if the __to_string method exists.
									if (
										in_array ('__to_string', $methods)
									)
										{
											// Invoke it via PHP Magic Functions.
											$data = (string) $data;
										}
									else
										{
											// Write the class name.
											$data = get_class ($data);
										}
									break;

								// Return null instead of a string.
								default:
									$data = null;
									break;
							}
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_text
		// Title: Parse output data for display.
		// Variables: $data
		//	1.	$data: The data to be parsed for output.
		// Description: Security and functionality function, clears all slashes from any external input for storage inside of memory.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function clean_data ($data)
			{
				if (
					is_string ($data)
				)
					{
						// Check if magic_quotes_gpc is on.
						if (
							get_magic_quotes_gpc () and
							// Fix for PHP 5.2.7.
							version_compare (PHP_VERSION, '5.2.7', '!=')
						)
							{
								$data = stripslashes ($data);
							}
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_data
		// Title: Parse input data for string.
		// Variables: $data, $is_input
		//	1.	$data: The data to be parsed for storage in PHP files.
		//	2.	$is_input: Whether or not this is unfiltered input.
		//		Default: false
		// Description: Security and functionality function, allows you to store any data inside of a single-quoted PHP string.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function parse_data ($data, $is_input = false)
			{
				if (
					is_string ($data)
				)
					{
						// Check if magic_quotes_gpc is on.
						if (
							$is_input and
							get_magic_quotes_gpc () and
							// Fix for PHP 5.2.7.
							version_compare (PHP_VERSION, '5.2.7', '!=')
						)
							{
								$data = stripslashes ($data);
							}

						// Our custom "addslashes" specific to single quoted strings.
						$data = str_replace (array ('\\', '\''), array ('\\\\', '\\\''), $data);
					}

				return $data;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: parse_file
		// Title: Create filenames.
		// Variables: $data
		//	1.	$data: The data to be converted into a safe filename.
		// Description: Converts a name into a safe filename.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function parse_file ($data)
			{
				// Encode into safe filename.
				$file = base64_encode ($data);
				$file = str_replace ('/', '-', $file);

				// Fix for case-insensitive filesystems.
				$matches = array ();
				preg_match_all ('/[A-Z]/', $file, $matches);
				$matches = '_' . implode ($matches [0]);
				$file .= $matches;
				$file = strtolower ($file);

				return $file;
			}

		/* --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- //
		// Name: collect_garbage
		// Title: Purge old information.
		// Variables: None.
		// Description: Scans the room list for old rooms, and deletes them from secondary storage. Removes levels that are old as well.
		// --|-- ----- ----- ----- -----|----- ----- ----- ----- --|-- */
		private static function collect_garbage ()
			{
				// Lock memory.
				molten_chat_cache :: lock_memory (true);

				$cleanup = molten_chat_cache :: global_memory_get ('garbage');

				// Check if there is even a time set.
				if (
					!is_null ($cleanup)
				)
					{
						if (
							(time () - $cleanup) < self :: $threshold
						)
							{
								// Release memory lock.
								molten_chat_cache :: lock_memory (false);

								return false;
							}
					}
				else
					{
						self :: $comments [] = 'collect_garbage: missing last garbage cleanup time.';
					}

				self :: $comments [] = 'collect_garbage: ran garbage collection.';

				molten_chat_cache :: global_memory_set ('garbage', time ());

				// Collect garbage here.
				
				// Cleanup instances.
				$instances = molten_chat_cache :: global_memory_get ('instances');

				$max = array (
					'current' => (time () - self :: $update * 5),
					'old' => (time () - self :: $update)
				);

				foreach ($instances as $identifier => &$instance)
					{
						arsort ($instance);

						$current = array_slice ($instance, 0, self :: $instances, true);
						$old = array_slice ($instance, self :: $instances, null, true);

						// Remove current values.
						foreach ($current as $key => $value)
							{
								if (
									$value < $max ['current']
								)
									{
										unset ($old [$key]);
									}
							}

						// Remove old values.
						foreach ($old as $key => $value)
							{
								if (
									$value < $max ['old']
								)
									{
										unset ($old [$key]);
									}
							}

						// Reset $instance.
						$instance = array ();

						// Array merge while maintaining indexed keys.
						foreach ($current as $key => $value)
							{
								$instance [$key] = $value;
							}

						// Array merge while maintaining indexed keys.
						foreach ($old as $key => $value)
							{
								$instance [$key] = $value;
							}

						if (
							!sizeof ($instance)
						)
							{
								// Remove this identifier.
								unset ($instances [$identifier]);
							}
					}
				unset ($instance);

				molten_chat_cache :: global_memory_set ('instances', $instances);

				// Release memory lock.
				molten_chat_cache :: lock_memory (false);

				return true;
			}
	}
